package com.imooc.feeds.service;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import com.google.common.collect.Lists;
import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.constant.RedisKeyConstant;
import com.imooc.commons.exception.ParameterException;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.model.vo.Feeds;
import com.imooc.commons.model.vo.FeedsVO;
import com.imooc.commons.model.vo.ShortDinerInfo;
import com.imooc.commons.model.vo.SignInDinerInfo;
import com.imooc.commons.utils.AssertUtil;
import com.imooc.feeds.mapper.FeedsMapper;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;

/**
 * \* @author: bear
 * \* Date: 2021-08-05-10:18
 * \* describe:
 */
@Service
public class FeedsService {

	@Value("${service.name.ms-oauth-server}")
	private String oauthServerName;
	@Value("${service.name.ms-diners-server}")
	private String dinersServerName;
	@Value("${service.name.ms-follow-server}")
	private String followServerName;

	@Resource
	private RestTemplate restTemplate;
	@Resource
	private RedisTemplate redisTemplate;
	@Resource
	private FeedsMapper feedsMapper;

	/**
	 * 添加说说，
	 * @param feeds
	 * @param accessToken
	 */
	@Transactional(rollbackFor = Exception.class)
	public void create(Feeds feeds,String accessToken){
		//非空校验
		AssertUtil.isNotEmpty(feeds.getContent(),"请输入内容");
		AssertUtil.isTrue(feeds.getContent().length() >255,"字数超过限制，请重新输入");
		//获取登录信息
		SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
		// Feed管理用户信息
		feeds.setFkDinerId(dinerInfo.getId());
		//sql添加
		int count = feedsMapper.save(feeds);
		AssertUtil.isTrue(count == 0,"添加失败");

		//推送到粉丝的列表中，后续这里采用异步消息队列解决性能问题
		List<Integer> followers = findFollowers(dinerInfo.getId());

		//推送feeds，以时间作为分数存储至zset
		long now = System.currentTimeMillis();
		followers.forEach(follower ->{
			String key = RedisKeyConstant.following_feeds.getKey() + follower;
			redisTemplate.opsForZSet().add(key,feeds.getId(),now);
		});
	}

	/**
	 * 变更feed
	 * @param followingDinerId  关注的好友id
	 * @param accessToken   登录用户token
	 * @param type  1 关注，0取关
	 */
	@Transactional(rollbackFor = Exception.class)
	public void addFollowingFeeds(Integer followingDinerId,String accessToken,int type){
		//请选择关注的好友
		AssertUtil.isTrue(followingDinerId == null || followingDinerId <1,"请选择关注的好友");
		//获取登录信息
		SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
		// 获取关注/取关食客所有feed
		List<Feeds> feedsList = feedsMapper.findByDinerId(followingDinerId);
		if(feedsList == null || feedsList.isEmpty()){
			return;
		}
		//我关注的好友feedsKey
		String key = RedisKeyConstant.following_feeds.getKey() + dinerInfo.getId();
		if(type == 0){
			//取关
			List<Integer> collect = feedsList.stream()
					.map(x -> x.getId())
					.collect(Collectors.toList());
			redisTemplate.opsForZSet().remove(key,collect.toArray(new Integer[]{}));
		}else{
			//关注
			Set<DefaultTypedTuple> typedTuples = feedsList.stream()
					.map(x -> new DefaultTypedTuple(x.getId(), (double)x.getUpdateDate().getTime()))
					.collect(Collectors.toSet());
			redisTemplate.opsForZSet().add(key,typedTuples);
		}
	}

	public void delete(Integer id,String accessToken){
		//请选择要删除的feed
		AssertUtil.isTrue(id == null || id<1,"请选择要删除的说说");
		//获取登录信息
		SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
		//获取feed内容
		Feeds feeds = feedsMapper.findById(id);
		//判断feed是否已经被删除且只能删除自己的
		AssertUtil.isTrue(feeds == null,"该信息已被删除");
		AssertUtil.isTrue(!feeds.getFkDinerId().equals(dinerInfo.getId()),"当前登录人只能删除自己的信息");
		//删除
		int count = feedsMapper.delete(id);
		if(count ==0){
			return;
		}
		// 将内容从粉丝的集合中删除， --异步消息队列优化
		// 先获取我的粉丝
		List<Integer> followers = findFollowers(dinerInfo.getId());
		followers.forEach(x->{
			String key = RedisKeyConstant.following_feeds.getKey() + x;
			redisTemplate.opsForZSet().remove(key,feeds.getId());
		});
	}

	private List<Integer> findFollowers(Integer id) {
		String url = followServerName + "/followers?dinerId={id}";
		ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, id);
		if(resultInfo.getCode() != ApiConstant.SUCCESS_CODE){
			throw new ParameterException(resultInfo.getCode(), resultInfo.getMessage());
		}
		List<Integer> data = (List)resultInfo.getData();
		return data;
	}

	private SignInDinerInfo loadSignInDinerInfo(String accessToken){
		//是否有token
		AssertUtil.mustLogin(accessToken);
		String url = oauthServerName +"/user/me?access_token={accessToken}" ;
		ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
		if(resultInfo.getCode() != ApiConstant.SUCCESS_CODE){
			throw new ParameterException(resultInfo.getCode(), resultInfo.getMessage());
		}
		//这里的data是一个linkedHashmap
		SignInDinerInfo dinerInfo = BeanUtil
				.fillBeanWithMap((LinkedHashMap)resultInfo.getData(), new SignInDinerInfo(), false);
		return dinerInfo;
	}

	/**
	 * 根据时间由近到远，每次查询 20 条 Feed
	 *
	 * @param page        页码
	 * @param accessToken 登录用户信息
	 * @return
	 */
	public List<FeedsVO> selectForPage(Integer page,String accessToken){
		if(page == null){
			page =1;
		}
		//获取登录信息
		SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
		//我关注的好友feedKey
		String key = RedisKeyConstant.following_feeds.getKey().concat(dinerInfo.getId() + "");
		// sortset的Zrevrange 命令是闭区间（左闭右闭）
		long start = (page -1) * ApiConstant.PAGE_SIZE;
		long end = page* ApiConstant.PAGE_SIZE -1;
		//获取20条feed id
		Set<Integer> feedIds = redisTemplate.opsForZSet().reverseRange(key, start, end);
		if(feedIds ==null || feedIds.isEmpty()){
			return Lists.newArrayList();
		}
		//根据多主键查feed
		List<Feeds> feeds = feedsMapper.findFeedsByIds(feedIds);
		//初始化关注好友id集合
		List<Integer> followingDinerIds = new ArrayList<>();
		//添加用户id至集合顺带将feeds转为vo对象
		List<FeedsVO> feedsVOList = feeds.stream().map(x -> {
			FeedsVO feedsVO = new FeedsVO();
			BeanUtil.copyProperties(x, feedsVO);
			followingDinerIds.add(x.getFkDinerId());
			return feedsVO;
		}).collect(Collectors.toList());
		//获取feed中用户信息
		ResultInfo result = restTemplate
				.getForObject(dinersServerName + "findByIds?access_token=${accessToken}&ids={ids}", ResultInfo.class,
						accessToken, StrUtil.join(",", followingDinerIds));
		if(result.getCode() !=ApiConstant.SUCCESS_CODE){
			throw new ParameterException(result.getCode(),result.getMessage());
		}
		List<LinkedHashMap> dinerMap = (ArrayList) result.getData();
		//构建一个key为用户id，value为shortDinerinfo的map
		Map<Integer, ShortDinerInfo> dinerinfos = dinerMap.stream().collect(Collectors.toMap(diner -> (Integer)diner.get("id"),
				diner -> BeanUtil.fillBeanWithMap(diner, new ShortDinerInfo(), true)));
		//循环vo集合，根据用户id从map中获取用户信息并设置至vo对象
		feedsVOList.forEach(x->x.setDinerInfo(dinerinfos.get(x.getFkDinerId())));
		return feedsVOList;
	}
}
